/**
 * Copyright 2010 Paulo Alem 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package flatfile;

import static flatfile.util.StringUtil.ucFirst;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import flatfile.annotations.Column;
import flatfile.annotations.DateFormat;
import flatfile.annotations.Entry;

/**
 * 
 * @author paulo
 */
public class EntryProcessor {
	private String data;

	public EntryProcessor from(String data) {
		this.data = data;
		return this;
	}

	// @DarkMagic
	public <T> T build(Class<T> cname) {
		if (!cname.isAnnotationPresent(Entry.class)) {
			throw new IllegalArgumentException("Class is not an @Entry");
		}

		if (data == null || data.isEmpty()) {
			throw new IllegalStateException("Invalid data");
		}

		if (cname.getAnnotation(Entry.class).enforceSize()
				&& cname.getAnnotation(Entry.class).size() != data.length()) {
			throw new IllegalStateException(
					"The data to be processed has a different size than the one specified in the @Entry class");
		}

		T t = null;
		try {
			t = cname.newInstance();
			for (Field field : t.getClass().getDeclaredFields()) {
				Column col = field.getAnnotation(Column.class);

				Class<?> fieldClass = field.getType();
				Method method = cname.getDeclaredMethod("set"
						+ ucFirst(field.getName()), fieldClass);
				String slice = null;

				if (col.end() < 0) {
					slice = data.substring(col.start());
				} else {
					slice = data.substring(col.start(), col.end());
				}
				if (col.converter() != Column.DEFAULT.class) {
					Converter c = col.converter().newInstance();
					method.invoke(t, c.convert(slice, fieldClass));
				} else {
					if (fieldClass.equals(String.class)) {
						method.invoke(t, slice);
					} else if (fieldClass.equals(int.class)) {
						method.invoke(t, Integer.parseInt(slice));
					} else if (fieldClass.equals(Long.class)) {
						method.invoke(t, Long.parseLong(slice));
					} else if (fieldClass.equals(BigDecimal.class)) {
						method.invoke(t, new BigDecimal(slice));
					} else if (fieldClass.equals(Date.class)) {
						if (field.isAnnotationPresent(DateFormat.class)) {
							String fmt = field.getAnnotation(DateFormat.class)
									.value();
							java.text.DateFormat df = new SimpleDateFormat(fmt);
							try {
								Date date = df.parse(slice);
								method.invoke(t, date);
							} catch (ParseException ex) {
								Logger
										.getLogger(
												EntryProcessor.class.getName())
										.log(Level.SEVERE, "Invalid date", ex);
							}
						}
					}
				}
			}
		} catch (InstantiationException ex) {
			Logger.getLogger(EntryProcessor.class.getName()).log(Level.SEVERE,
					null, ex);
		} catch (IllegalAccessException ex) {
			Logger.getLogger(EntryProcessor.class.getName()).log(Level.SEVERE,
					null, ex);
		} catch (NoSuchMethodException ex) {
			Logger.getLogger(EntryProcessor.class.getName()).log(Level.SEVERE,
					"Method does not exist", ex);
		} catch (IllegalArgumentException ex) {
			Logger.getLogger(EntryProcessor.class.getName()).log(Level.SEVERE,
					null, ex);
		} catch (InvocationTargetException ex) {
			Logger.getLogger(EntryProcessor.class.getName()).log(Level.SEVERE,
					null, ex);
		}
		return t;
	}

}
